/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.internal.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Comparator;
import java.util.Date;

import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.resources.IChangelist;
import net.sourceforge.perforce.core.resources.IP4File;
import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.internal.Policy;
import net.sourceforge.perforce.ui.model.PerforceModelElement;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IActionFilter;
import org.eclipse.ui.model.IWorkbenchAdapter;

/**
 * @version $Revision: 489 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 *
 *         TODO: Move IWorkbenchAdapter methods to own class.
 */
public class ChangelistElement extends PerforceModelElement implements IChangelist,
    IWorkbenchAdapter, IAdaptable, IActionFilter {
  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the Perforce Server. */
  private IPerforceServer server;
  /** Stores the pending changelist cache. */
  private PendingChangelistCache pendingChangelistCache;
  /** Stores the id of the changelist. */
  private Integer id;
  /** Stores the changelist's description. */
  private String description;
  /** Stores the creation date. */
  private Date date;
  /** Stores the associated client's name. */
  private String clientName;
  /** Stores the creating user's name. */
  private String userName;

  // -------------------------------------------------------------------------
  // Public Constructors

  /**
   * Constructor for ChangelistElement.
   *
   * @param server reference to the Perforce server.
   * @param cache the PendingChangelistCache.
   * @param id the changelist's id.
   */
  public ChangelistElement(IPerforceServer server, PendingChangelistCache cache, Integer id) {
    this.server = server;
    pendingChangelistCache = cache;
    this.id = id;
  }

  // ---------------------------------------------------------------- Public
  // Methods (IChangelist)

  /** @see net.sourceforge.perforce.core.resources.IChangelist#getId() */
  public Integer getId() {
    return id;
  }

  /** @see net.sourceforge.perforce.core.resources.IChangelist#getClientName() */
  public String getClientName() {
    return clientName;
  }

  /** @see net.sourceforge.perforce.core.resources.IChangelist#getUserName() */
  public String getUserName() {
    return userName;
  }

  /** @see net.sourceforge.perforce.core.resources.IChangelist#getDate() */
  public Date getDate() {
    return date;
  }

  /** @see net.sourceforge.perforce.core.resources.IChangelist#getDescription() */
  public String getDescription() {
    return description;
  }

  public String getStatus() {
    return IChangelist.STATUS_PENDING;
  }

  public IP4File[] getFiles() {
    final IChangelist changelist = this;
    return pendingChangelistCache.getFileElements(new Comparator<IP4File>() {
      public int compare(IP4File o1, IP4File o2) {
        return 0;
      }

      @Override
      public boolean equals(Object obj) {
        IP4File file = (IP4File) obj;
        return file.getChangelistId().equals(changelist.getId())
            && file.getClientName().equals(changelist.getClientName());
      }
    });
  }


  /**
   * Call this method to retrieve the server to which this changelist element
   * belongs.
   *
   * @return the perforce server.
   */
  public IPerforceServer getServer() {
    return server;
  }

  // ---------------------------------------------------------- Public Methods
  // (IWorkbenchAdapter)

  /** @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(Object) */
  public Object[] getChildren(Object o) {
    if (!(o instanceof IChangelist)) {
      return new Object[0];
    }

    IChangelist changelist = (IChangelist) o;
    return changelist.getFiles();
  }

  /** @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(Object) */
  public ImageDescriptor getImageDescriptor(Object o) {
    if (!(o instanceof IChangelist)) {
      return null;
    }

    IChangelist changelist = (IChangelist) o;

    String imageId = IPerforceUIConstants.IMG_CLIENT_CHANGELIST;
    if (!changelist.getClientName().equals(server.getClient())) {
      imageId = IPerforceUIConstants.IMG_OTHER_CHANGELIST;
    }

    return PerforceUIPlugin.getPlugin().getImageDescriptor(imageId);
  }

  /** @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(Object) */
  public String getLabel(Object o) {
    if (!(o instanceof IChangelist)) {
      return null;
    }

    IChangelist changelist = (IChangelist) o;

    String shortendDescription = cleanup(changelist.getDescription());
    if (shortendDescription.length() >= 40) {
      shortendDescription = shortendDescription.substring(0, 39) + "..."; //$NON-NLS-1$
    }

    if (changelist.getClientName().equals(server.getClient())) {
      return Policy.bind("ChangelistElement.getLabel", //$NON-NLS-1$
          changelist.getId(), shortendDescription);
    } else {
      if (changelist.getId().equals(IChangelist.DEFAULT)) {
        return Policy.bind("ChangelistElement.getLabelOtherDefault", //$NON-NLS-1$
            changelist.getUserName(), changelist.getClientName());
      } else {
        Object[] args =
            new Object[] {changelist.getId(), changelist.getUserName(), changelist.getClientName(),
                shortendDescription};

        return Policy.bind("ChangelistElement.getLabelOther", args); //$NON-NLS-1$
      }
    }
  }

  public Object getParent(Object o) {
    return null;
  }

  // ----------------------------------------------------------------- Public
  // Methods (IAdaptable)


  @SuppressWarnings("unchecked")
  // adapter
  public Object getAdapter(Class adapter) {
    if (adapter == IWorkbenchAdapter.class) {
      return this;
    } else if (adapter == IPerforceServer.class) {
      return getServer();
    }

    return null;
  }

  // -------------------------------------------------------------- Public
  // Methods (IActionFilter)

  /** @see IActionFilter#testAttribute(Object, String, String) */
  public boolean testAttribute(Object target, String name, String value) {
    if (name.equals("clientName")) //$NON-NLS-1$
    {
      boolean expectedValue = !value.equalsIgnoreCase("other");
      IChangelist changelist = (IChangelist) target;

      boolean realValue = changelist.getClientName().equals(changelist.getServer().getClient());
      return realValue == expectedValue;

    } else if (name.equals("changelist")) //$NON-NLS-1$
    {
      Integer changelistId = null;
      if (value.equalsIgnoreCase("default")) {
        changelistId = IChangelist.DEFAULT;
      } else {
        try {
          changelistId = new Integer(value);
        } catch (NumberFormatException e) {
          PerforceUIPlugin.logError("Failed to parse value <" + value + ">", e); //$NON-NLS-1$ //$NON-NLS-1$
          return false;
        }
      }

      IChangelist changelist = (IChangelist) target;
      return changelist.getId().equals(changelistId);
    }

    return false;
  }

  // ---------------------------------------------------------------------
  // Public Methods (Object)

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }

    if (!(object instanceof IChangelist)) {
      return false;
    }

    IChangelist other = (IChangelist) object;

    boolean flag = getServer().equals(other.getServer());
    flag &= getId().equals(other.getId());

    return flag;
  }

  @Override
  public int hashCode() {
    return getId().hashCode() * getServer().hashCode();
  }

  //----------------------------------------------------------------------------
  // --------- Methods

  /**
   * Call this method to set the date of the changelist.
   *
   * @param date the date.
   */
  void setDate(Date date) {
    this.date = date;
  }

  /**
   * Call this method to set the description.
   *
   * @param description the description.
   */
  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * Sets the client name.
   *
   * @param clientName the name of the client.
   */
  void setClientName(String clientName) {
    this.clientName = clientName;
  }

  /**
   * Sets the user name.
   *
   * @param userName name of the user.
   */
  void setUserName(String userName) {
    this.userName = userName;
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  /**
   * Returns a version of text with CR/LF converted to spaces.
   *
   * @param text to convert.
   * @return the converted text.
   */
  private String cleanup(String text) {
    if (text == null) {
      return "";
    }

    StringBuilder result = new StringBuilder();
    BufferedReader reader = new BufferedReader(new StringReader(text));

    try {
      String line = null;
      while ((line = reader.readLine()) != null) {
        result.append(line).append(' ');
      }

      return result.toString().trim();
    } catch (IOException e) {
      handle(e);
    }

    return text;
  }

  /**
   * Create a text version of the changelist's id.
   *
   * @param changelist the changelist.
   * @return the text version of the id.
   */
  // TODO(rcleveng): Remove this?
  @SuppressWarnings("unused")
  private String createIdLabel(IChangelist changelist) {
    String id =
        (!changelist.getId().equals(IChangelist.DEFAULT)) ? changelist.getId().toString()
            : "Default"; //$NON-NLS-1$

    if (!changelist.getClientName().equals(server.getClient())) {
      id = id + " - " + changelist.getUserName() + '@' + changelist.getClientName();
    }

    return id;
  }
}
